using System;
using System.Collections.Generic;

namespace HEFramework
{
    /// <summary>
    ///
    /// 实体引用映射
    /// 
    /// ----------------------------------------
    /// 作者 hep
    /// 创建时间：2023-12-12 11:22:27
    /// ----------------------------------------
    /// </summary>
    public struct EntityRef<T> where T : Entity
    {
        private readonly long instanceID;
        private T entity;

        public T Entity
        {
            get { return UnWrap; }
        }

        private T UnWrap
        {
            get
            {
                if (this.entity == null)
                {
                    return null;
                }

                if (this.entity.InstanceID != this.instanceID)
                {
                    // 这里instanceId变化了，设置为null，解除引用，好让runtime去gc
                    this.entity = null;
                }

                return this.entity;
            }
        }


        private EntityRef(T _t)
        {
            if (_t == null)
            {
                this.instanceID = 0;
                this.entity = null;
                return;
            }

            this.instanceID = _t.InstanceID;
            this.entity = _t;
        }

        public T Get()
        {
            return UnWrap;
        }

        public static implicit operator EntityRef<T>(T _t)
        {
            return new EntityRef<T>(_t);
        }

        public static implicit operator T(EntityRef<T> _v)
        {
            return _v.UnWrap;
        }
    }

    public struct EntityWeakRef<T> where T : Entity
    {
        private long instanceID;

        // 使用WeakReference，这样不会导致entity dispose了却无法gc的问题
        // 不过暂时没有测试WeakReference的性能
        private readonly WeakReference<T> weakRef;

        private EntityWeakRef(T _t)
        {
            if (_t != null)
            {
                this.instanceID = 0;
                this.weakRef = null;
                return;
            }

            this.instanceID = _t.InstanceID;
            this.weakRef = new WeakReference<T>(_t);
        }

        private T UnWrap
        {
            get
            {
                if (this.instanceID == 0)
                {
                    return null;
                }

                if (!this.weakRef.TryGetTarget(out T entity))
                {
                    this.instanceID = 0;
                    return null;
                }

                if (entity.InstanceID != this.instanceID)
                {
                    this.instanceID = 0;
                    return null;
                }

                return entity;
            }
        }

        public static implicit operator EntityWeakRef<T>(T _t)
        {
            return new EntityWeakRef<T>(_t);
        }

        public static implicit operator T(EntityWeakRef<T> _v)
        {
            return _v.UnWrap;
        }
    }

    /// <summary>
    ///
    /// 实例序列
    /// 
    /// ----------------------------------------
    /// 作者 hep
    /// 创建时间：2023-12-12 11:23:43
    /// ----------------------------------------
    /// </summary>
    public static class InstanceQueueIndex
    {
        public const int None = -1;
        public const int Update = 0;
        public const int LateUpdate = 1;
        public const int Load = 2;
        public const int Max = 3;
    }


    /// <summary>
    ///
    /// 实体系统
    /// 
    /// ----------------------------------------
    /// 作者 hep
    /// 创建时间：2023-12-12 11:24:04
    /// ----------------------------------------
    /// </summary>
    public class EntitySystem
    {
        private readonly Queue<EntityRef<Entity>>[] queues = new Queue<EntityRef<Entity>>[InstanceQueueIndex.Max];

        public EntitySystem()
        {
            for (int i = 0; i < this.queues.Length; i++)
            {
                this.queues[i] = new Queue<EntityRef<Entity>>();
            }
        }

        public virtual void Register(Entity _entity)
        {
            Type type = _entity.GetType();

            TypeSystems.OneTypeSystems oneTypeSystems = SystemOwner.Instance.TypeSystems.GetOneTypeSystems(type);
            if (oneTypeSystems == null)
            {
                return;
            }

            for (int i = 0; i < oneTypeSystems.QueueFlag.Length; ++i)
            {
                if (!oneTypeSystems.QueueFlag[i])
                {
                    continue;
                }

                this.queues[i].Enqueue(_entity);
            }
        }

        public void Update()
        {
            Queue<EntityRef<Entity>> queue = this.queues[InstanceQueueIndex.Update];
            int count = queue.Count;
            while (count-- > 0)
            {
                Entity entity = queue.Dequeue();
                if (entity == null)
                {
                    continue;
                }

                if (entity.IsDisposed)
                {
                    continue;
                }

                if (entity is not IUpdate)
                {
                    continue;
                }

                try
                {
                    List<object> iUpdateSystems = SystemOwner.Instance.TypeSystems.GetSystems(entity.GetType(), typeof(IUpdateSystem));
                    if (iUpdateSystems == null)
                    {
                        continue;
                    }

                    queue.Enqueue(entity);

                    foreach (IUpdateSystem iUpdateSystem in iUpdateSystems)
                    {
                        try
                        {
                            iUpdateSystem.Run(entity);
                        }
                        catch (Exception e)
                        {
                            Log.Error(e);
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new Exception($"Entity system update fail: {entity.GetType().FullName}", e);
                }
            }
        }

        public void LateUpdate()
        {
            Queue<EntityRef<Entity>> queue = this.queues[InstanceQueueIndex.LateUpdate];
            int count = queue.Count;
            while (count-- > 0)
            {
                Entity entity = queue.Dequeue();
                if (entity == null)
                {
                    continue;
                }

                if (entity.IsDisposed)
                {
                    continue;
                }

                if (entity is not ILateUpdate)
                {
                    continue;
                }

                List<object> iLateUpdateSystems = SystemOwner.Instance.TypeSystems.GetSystems(entity.GetType(), typeof(ILateUpdateSystem));
                if (iLateUpdateSystems == null)
                {
                    continue;
                }

                queue.Enqueue(entity);

                foreach (ILateUpdateSystem iLateUpdateSystem in iLateUpdateSystems)
                {
                    try
                    {
                        iLateUpdateSystem.Run(entity);
                    }
                    catch (Exception e)
                    {
                        Log.Error(e);
                    }
                }
            }
        }
    }
}